//Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Text.RegularExpressions;

namespace Microsoft.Office.OneNote
{
	/// <exclude/>
	/// <summary>
	/// Most hierarchy and page object nodes have their own ObjectId, which can be used to 
	/// uniquely identify the node across sessions.
	/// The object id consists of GUID, a version number, and the source data, e.g.:
	/// {12345678-1234-1234-1234-123456789012}{0}{B0}
	/// </summary>
	[Serializable]
	public class ObjectId
	{
		#region Constructors
		/// <summary>
		/// Creates a new random <see cref="ObjectId"/>.
		/// </summary>
		public ObjectId()
		{
		}

		/// <summary>
		/// Instantiates an <see cref="ObjectId"/> from a previously serialized
		/// instance.
		/// </summary>
		/// <param name="serialized">
		/// The serialized ObjectId, in string format.
		/// </param>
		public ObjectId(string serialized)
		{
			if (serialized == null || serialized.Length == 0)
			{
				throw (new ArgumentException("Unable to parse emptry string."));
			}

			guid = new Guid(Regex.Match(serialized, guidRegex).ToString());
			version = Regex.Match(serialized, versionRegex).ToString();
			source = Regex.Match(serialized, sourceRegex).ToString();

			if (guid.Equals(Guid.Empty))
			{
				throw (new ArgumentException("Invalid GUID argument."));
			}
		}
		#endregion

		/// <summary>
		/// Returns the ObjectId in a string format.
		/// </summary>
		/// <returns>The ObjectId in a string format.</returns>
		public override string ToString()
		{
			if (guid != Guid.Empty && guid.ToString().Length > 0)
				return '{' + guid.ToString() + "}" + version + source;
			else
				return "";
		}

		/// <summary>
		/// Returns true if the specified object is an ObjectId and represents
		/// the same unique identifier.
		/// </summary>
		/// <param name="obj">The object to be compared.</param>
		/// <returns>
		/// true if the object is an ObjectId representing the same unique 
		/// identifier.
		/// </returns>
		public override bool Equals(object obj)
		{
			if (obj == this)
				return true;

			if (!(obj is ObjectId))
				return false;

			ObjectId objId = obj as ObjectId;

			if (!guid.Equals(objId.guid))
				return false;

			if (version != null && !version.Equals(objId.version))
				return false;
			else if (version == null && objId.version != null)
				return false;

			if (source != null && !source.Equals(objId.source))
				return false;
			else if (source == null && objId.source != null)
				return false;

			return true;
		}

		/// <summary>
		/// Serves as a hash function for the <see cref="ObjectId"/>, suitable for use 
		/// in hashing algorithms and data structures like a hash table.  
		/// </summary>
		/// <returns>A hash code for the current <see cref="ObjectId"/>.</returns>
		public override int GetHashCode()
		{
			return guid.GetHashCode() + version.GetHashCode() + source.GetHashCode();
		}

		/// <summary>
		/// Each object has it's own unique GUID.
		/// </summary>
		private Guid guid;

		/// <summary>
		/// The revision of the object, incrmented every time it's modified.
		/// </summary>
		private string version;

		/// <summary>
		/// An extra bit of information, used by OneNote to distinguish the source.
		/// </summary>
		private string source;

		/// Regexes used in parsing:
		private const string guidRegex = "\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}";
		private const string versionRegex = "\\{[0-9]+\\}";
		private const string sourceRegex = "\\{[A-Z][0-9]+\\}$";

		/// <summary>
		/// The Empty ObjectId.  Used by the NotebookRoot hierarchy node.
		/// </summary>
		public static ObjectId Empty
		{
			get
			{
				if (empty == null)
				{
					empty = new ObjectId();
					empty.guid = Guid.Empty;
					empty.version = null;
					empty.source = null;
				}

				return empty;
			}
		}

		private static ObjectId empty;
	}
}